package com.xiaoyu.base.app;

import android.content.Context;

import com.xiaoyu.base.BaseConstants;
import com.xiaoyu.base.log.AppPerformanceLogClient;

import in.srain.cube.app.init.InitializableAsync;
import in.srain.cube.app.init.InitializableOneWay;
import in.srain.cube.app.init.InitializableSync;
import in.srain.cube.concurrent.AppThreads;
import in.srain.cube.util.log.LogEvent;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import in.srain.cube.concurrent.SimpleTask;
import in.srain.cube.util.CLog;

public class AppEngine {

    private static final int STATE_INIT = 1;
    private static final int STATE_DOING = 2;
    private static final int STATE_DONE = 3;

    private static final String LOG_TAG = "AppEngine";

    private static AppEngine sInstance;
    private int mState = STATE_INIT;
    private Queue<Runnable> mCallbackList;

    private List<InitializableSync> mSyncList;
    private List<InitializableAsync> mAsyncList;
    private List<InitializableOneWay> mOneWayList;

    private AppEngine() {
        mCallbackList = new LinkedList<>();
        mSyncList = new LinkedList<>();
        mAsyncList = new LinkedList<>();
        mOneWayList = new LinkedList<>();
    }

    public static synchronized AppEngine getInstance() {
        if (sInstance == null) {
            sInstance = new AppEngine();
        }
        return sInstance;
    }

    public synchronized void addSyncInitComponents(InitializableSync initializable) {
        if (mState != STATE_INIT) {
            throw new IllegalStateException("You can not add component when the state is not STATE_INIT: " + initializable);
        }
        mSyncList.add(initializable);
    }

    public synchronized void addAsyncInitComponents(InitializableAsync initializable) {
        if (mState != STATE_INIT) {
            throw new IllegalStateException("You can not add component when the state is not STATE_INIT: " + initializable);
        }
        mAsyncList.add(initializable);
    }

    public synchronized void addOneWayInitComponents(InitializableOneWay initializableOneWay) {
        if (mState != STATE_INIT) {
            throw new IllegalStateException("You can not add component when the state is not STATE_INIT: " + initializableOneWay);
        }
        mOneWayList.add(initializableOneWay);
    }

    public synchronized void addFinishInitialJobCallback(Runnable runnable) {
        if (mState == STATE_DONE) {
            CLog.w(LOG_TAG, "runAfterInitDone, but all the components have been initiated, invoke callback directly: %s", runnable);
            if (runnable != null) {
                runnable.run();
            }
        } else {
            mCallbackList.add(runnable);
        }
    }

    public synchronized void start(final Context context) {
        if (mState == STATE_DOING || mState == STATE_DONE) {
            return;
        }
        CLog.i(LOG_TAG, "start");
        mState = STATE_DOING;

        final LogEvent logEvent = new LogEvent("init");
        logEvent.startTimeForKey("init-sync");
        initSync(context);
        logEvent.endTimeForKey("init-sync");

        AppThreads.runOnInitThread(new SimpleTask() {
            @Override
            public void doInBackground() {
                logEvent.startTimeForKey("init-async");
                initAsync(context);
                logEvent.endTimeForKey("init-async");
                AppPerformanceLogClient.Companion.getInstance().addLog(logEvent);
            }

            @Override
            public void onFinish(boolean canceled) {
                CLog.i(BaseConstants.LOG_TAG_PERFORMANCE, "onFinish: %s", logEvent);
                finish();

                AppThreads.runOnInitThread(new Runnable() {
                    @Override
                    public void run() {
                        initiateAsyncOneWay(context);
                    }
                });
            }
        });
    }

    private void initSync(Context context) {
        CLog.i(LOG_TAG, "start initSync");
        if (mSyncList == null) {
            return;
        }
        for (InitializableSync item : mSyncList) {
            CLog.i(LOG_TAG, "initSync %s", item);
            item.initiateSync(context);
        }
        mSyncList.clear();
    }

    private void initAsync(Context context) {
        CLog.i(LOG_TAG, "start initAsync");
        for (InitializableAsync item : mAsyncList) {
            CLog.i(LOG_TAG, "initAsync %s", item);
            item.initiateAsync(context);
        }
        mAsyncList.clear();
    }

    private void initiateAsyncOneWay(Context context) {
        CLog.i(LOG_TAG, "start initiateAsyncOneWay");
        if (mOneWayList == null) {
            return;
        }
        for (InitializableOneWay item : mOneWayList) {
            CLog.i(LOG_TAG, "initiateAsyncOneWay %s", item);
            item.initiateAsyncOneWay(context);
        }
        mOneWayList.clear();
    }

    private synchronized void finish() {
        CLog.i(LOG_TAG, "finish");
        mAsyncList = null;
        mState = STATE_DONE;
        while (!mCallbackList.isEmpty()) {
            Runnable runnable = mCallbackList.poll();
            if (runnable != null) {
                runnable.run();
            }
        }
    }

    public boolean hasFinishedInitialJob() {
        return mState == STATE_DONE;
    }
}